﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

namespace Xrm.AlmFramework.Console
{
    /// <summary>
    /// A class to manage a visual studio project
    /// </summary>
    public class VisualStudioProject
    {
        private string filePath;
        private string _directory, _fileName;
        private System.Xml.XmlDocument _content;
        private XmlNamespaceManager nsmgr;


        #region ---------------------------------------- Constructor ------------------------------------------------

        public VisualStudioProject(string filePath)
        {
            this.filePath = filePath;
            this._directory = System.IO.Path.GetDirectoryName(filePath);
            this._fileName = System.IO.Path.GetFileName(filePath);
        }

        #endregion

        #region ---------------------------------------- Properties -------------------------------------------------

        public string Directory
        {
            get
            {
                return _directory;
            }
        }

        public string FileName
        {
            get
            {
                return _fileName;
            }
        }

        public System.Xml.XmlDocument Content
        {
            get
            {
                if (_content == null) Load();
                return _content;
            }
        }

        #endregion

        #region ---------------------------------------- Methods ----------------------------------------------------

        private void Load()
        {
            _content = new System.Xml.XmlDocument();
            _content.Load(this.filePath);

            // Set namespace manager
            nsmgr = new XmlNamespaceManager(_content.NameTable);
            nsmgr.AddNamespace("ns", @"http://schemas.microsoft.com/developer/msbuild/2003");
        }

        /// <summary>
        /// Recursively adds folders and files to a cs project based on the directory path provided.
        /// </summary>
        /// <param name="directoryPath"></param>
        public void AddFiles(string directoryPath)
        {
            // get the path relative to the folder to add to the csproj
            AddFolder(GetRelativePath(directoryPath));

            foreach (string f in System.IO.Directory.GetFiles(directoryPath))
            {
                // get the path relative to the file to add to the csproj
                AddFile(GetRelativePath(f));
            }

            foreach (string dir in System.IO.Directory.GetDirectories(directoryPath))
            {
                AddFiles(dir);
            }
        }

        public void Remove(List<CrmSolutionAnalyser.Target> targets)
        {
            foreach (CrmSolutionAnalyser.Target target in targets)
            {
                if (target.Type == CrmSolutionAnalyser.Target.TargetType.File)
                {
                    if (Content.SelectSingleNode(@"//ns:Project/ns:ItemGroup/ns:Content[@Include='" + GetRelativePath(target.Path) + "']", nsmgr) != null)
                    {
                        XmlNode node = Content.SelectSingleNode(@"//ns:Project/ns:ItemGroup/ns:Content[@Include='" + GetRelativePath(target.Path) + "']", nsmgr);
                        node.ParentNode.RemoveChild(node);
                    }
                }
                else
                {
                    foreach (XmlNode folderNode in Content.SelectNodes(@"//ns:Project/ns:ItemGroup/ns:Folder[contains(@Include, '" + GetRelativePath(target.Path) + "')]", nsmgr))
                    {
                        folderNode.ParentNode.RemoveChild(folderNode);
                    }

                    foreach (XmlNode fileNode in Content.SelectNodes(@"//ns:Project/ns:ItemGroup/ns:Content[contains(@Include, '" + GetRelativePath(target.Path) + "')]", nsmgr))
                    {
                        fileNode.ParentNode.RemoveChild(fileNode);
                    }
                }
            }
        }

        private string GetRelativePath(string path)
        {
            string retVal;
            retVal = path.Replace(Directory, "");
            if (retVal.EndsWith(@"\"))
            {
                // Trim trailing slash
                retVal = (retVal.LastIndexOf(@"\") > -1) ? retVal.Substring(0, retVal.LastIndexOf(@"\")) : retVal;
            }
            // Trim starting slash
            retVal = (retVal.IndexOf(@"\") == 0) ? retVal.Substring(1) : retVal;
            return retVal;
        }

        private void AddFolder(string relativeFolderPath)
        {
            XmlNode parentNode = GetRootFolderNode();
            XmlNode node = Content.SelectSingleNode(@"//ns:Project/ns:ItemGroup/ns:Folder[@Include='" + relativeFolderPath + "']", nsmgr);
            if (node == null)
            {
                //Insert content node
                XmlDocumentFragment frag = Content.CreateDocumentFragment();
                frag.InnerXml = @"<Folder Include='" + relativeFolderPath + "' xmlns=''/>";
                parentNode.AppendChild(frag);
            }
        }

        private void AddFile(string relativepath)
        {
            XmlNode parentNode = GetRootContentNode();
            XmlNode node = Content.SelectSingleNode(@"//ns:Project/ns:ItemGroup/ns:Content[@Include='" + relativepath + "']", nsmgr);
            if (node == null)
            {
                //Insert content node
                XmlDocumentFragment frag = Content.CreateDocumentFragment();
                frag.InnerXml = @"<Content Include='" + relativepath + "' xmlns=''><SubType>Content</SubType></Content>";
                parentNode.AppendChild(frag);
            }
        }

        private XmlNode GetRootFolderNode()
        {
            if (Content.SelectSingleNode(@"//ns:Project/ns:ItemGroup[ns:Folder]", nsmgr) == null)
            {
                XmlNode rootNode = Content.SelectSingleNode(@"//ns:Project", nsmgr);
                XmlNode contentNode = Content.CreateNode("element", "ItemGroup", nsmgr.DefaultNamespace);
                rootNode.AppendChild(contentNode);
                return contentNode;
            }
            else
            {
                return Content.SelectSingleNode(@"//ns:Project/ns:ItemGroup[ns:Folder]", nsmgr);
            }
        }

        private XmlNode GetRootContentNode()
        {
            if (Content.SelectSingleNode(@"//ns:Project/ns:ItemGroup[ns:Content]", nsmgr) == null)
            {
                if (Content.SelectSingleNode(@"//ns:Project/ItemGroup[Content]", nsmgr) == null)
                {
                    XmlNode rootNode = Content.SelectSingleNode(@"//ns:Project", nsmgr);
                    XmlNode contentNode = Content.CreateNode("element", "ItemGroup", nsmgr.DefaultNamespace);
                    rootNode.AppendChild(contentNode);
                    return contentNode;
                }
                else
                {
                    return Content.SelectSingleNode(@"//ns:Project/ItemGroup[Content]", nsmgr);
                }
            }
            else
            {
                return Content.SelectSingleNode(@"//ns:Project/ns:ItemGroup[ns:Content]", nsmgr);
            }
        }

        public void Save()
        {
            string xml = Content.OuterXml.Replace("xmlns=\"\"", "");
            Content.LoadXml(xml);
            Content.Save(this.filePath);
        }

        #endregion

    }
}
